#pragma once

#include <AVUIRoutedPropertyChanged.h>
#include <AVUIHeaderedItemsControl.h>
#include <AVUISelector.h>

namespace AVUI {

class Popup;
class DispatcherTimer;
class Menu;

class MenuItem : public HeaderedItemsControl
{
    friend class MenuBase;
    friend class ContextMenu;
    friend class Menu;
public:

    MenuItem() : m_userInitiatedPress(false) { }
    ~MenuItem();

    DECLARE_ELEMENT(MenuItem, HeaderedItemsControl);

    virtual void OnApplyTemplate();


    IMPLEMENT_ROUTED_EVENT(Click, RoutedEventArgs, RoutingStrategy::Bubble);
    IMPLEMENT_ROUTED_EVENT(PreviewClick, RoutedEventArgs, RoutingStrategy::Bubble);
    IMPLEMENT_ROUTED_EVENT(Checked, RoutedEventArgs, RoutingStrategy::Bubble);
    IMPLEMENT_ROUTED_EVENT(Unchecked, RoutedEventArgs, RoutingStrategy::Bubble);
    IMPLEMENT_ROUTED_EVENT(SubmenuOpened, RoutedEventArgs, RoutingStrategy::Bubble);
    IMPLEMENT_ROUTED_EVENT(SubmenuClosed, RoutedEventArgs, RoutingStrategy::Bubble);

    DECLARE_READONLY_DEPENDENCY_PROPERTY(IsPressed)
    bool get_IsPressed() { return AVUI::UnboxValue<bool>(GetValue(get_IsPressedProperty())); }

    DECLARE_READONLY_DEPENDENCY_PROPERTY(IsHighlighted)
    bool get_IsHighlighted() { return AVUI::UnboxValue<bool>(GetValue(get_IsHighlightedProperty())); }

    DECLARE_READONLY_DEPENDENCY_PROPERTY(IsSuspendingPopupAnimation)
    bool get_IsSuspendingPopupAnimation() { return AVUI::UnboxValue<bool>(GetValue(get_IsSuspendingPopupAnimationProperty())); }

    DECLARE_READONLY_DEPENDENCY_PROPERTY(Role)
    MenuItemRole::Enum get_Role() { return (MenuItemRole::Enum) AVUI::UnboxValue<int>(GetValue(get_RoleProperty())); }

    DECLARE_DEPENDENCY_PROPERTY(Command);
    TRefCountedPtr<RoutedCommand> get_Command() { return (RoutedCommand*)(Object*)GetValue(get_CommandProperty()); }
    void set_Command(RoutedCommand* pValue) { SetValue(get_CommandProperty(), (Object*)pValue); } 

    DECLARE_DEPENDENCY_PROPERTY(CommandParameter);
    TRefCountedPtr<Object> get_CommandParameter() { return GetValue(get_CommandParameterProperty()); }
    void set_CommandParameter(Object* pValue) { SetValue(get_CommandParameterProperty(), pValue); } 

    DECLARE_DEPENDENCY_PROPERTY(CommandTarget);
    TRefCountedPtr<Object> get_CommandTarget() { return GetValue(get_CommandTargetProperty()); }
    void set_CommandTarget(Object* pValue) { SetValue(get_CommandTargetProperty(), pValue); } 

    DECLARE_DEPENDENCY_PROPERTY(IsCheckable);
    bool get_IsCheckable() { return AVUI::UnboxValue<bool>(GetValue(get_IsCheckableProperty())); }
    void set_IsCheckable(bool value) { SetValue(get_IsCheckableProperty(), AVUI::BoxValue(value)); } 

    DECLARE_DEPENDENCY_PROPERTY(IsChecked);
    bool get_IsChecked() { return AVUI::UnboxValue<bool>(GetValue(get_IsCheckedProperty())); }
    void set_IsChecked(bool value) { SetValue(get_IsCheckedProperty(), AVUI::BoxValue(value)); } 

    DECLARE_DEPENDENCY_PROPERTY(IsSubmenuOpen);
    bool get_IsSubmenuOpen() { return AVUI::UnboxValue<bool>(GetValue(get_IsSubmenuOpenProperty())); }
    void set_IsSubmenuOpen(bool value) { SetValue(get_IsSubmenuOpenProperty(), AVUI::BoxValue(value)); } 

    DECLARE_DEPENDENCY_PROPERTY(StaysOpenOnClick);
    bool get_StaysOpenOnClick() { return AVUI::UnboxValue<bool>(GetValue(get_StaysOpenOnClickProperty())); }
    void set_StaysOpenOnClick(bool value) { SetValue(get_StaysOpenOnClickProperty(), AVUI::BoxValue(value)); } 

    DECLARE_DEPENDENCY_PROPERTY(InsideContextMenu);

    DECLARE_DEPENDENCY_PROPERTY(Icon);
    TRefCountedPtr<Object> get_Icon() { return GetValue(get_IconProperty()); }
    void set_Icon(Object* pValue) { SetValue(get_IconProperty(), pValue); } 

protected:

    virtual TRefCountedPtr<DependencyObject> GetContainerForItemOverride();
    virtual bool IsItemItsOwnContainerOverride(Object* pItem);
    virtual void PrepareContainerForItemOverride(DependencyObject* pElement, Object* pItem);
    virtual bool ShouldApplyItemContainerStyle(DependencyObject* pContainer, Object* pItem);
    virtual void OnChecked(RoutedEventArgs* pArgs) { RaiseEvent(pArgs); }
    virtual void OnClick() { OnClickImpl(false); }

    virtual void OnInitialized(EventArgs* pEventArgs);
    virtual void OnItemsChanged(CollectionChangedEventArgs* pArgs);
    virtual void OnKeyDown(KeyEventArgs* pArgs);
    virtual void OnMouseEnter(MouseEventArgs* pArgs);
    virtual void OnMouseLeave(MouseEventArgs* pArgs);
    virtual void OnMouseLeftButtonDown(MouseButtonEventArgs* pArgs);
    virtual void OnMouseLeftButtonUp(MouseButtonEventArgs* pArgs);
    virtual void OnMouseMove(MouseEventArgs* pArgs);
    virtual void OnMouseRightButtonDown(MouseButtonEventArgs* pArgs);
    virtual void OnMouseRightButtonUp(MouseButtonEventArgs* pArgs);

    virtual void OnSubmenuClosed(RoutedEventArgs* pArgs) { RaiseEvent(pArgs); }
    virtual void OnSubmenuOpened(RoutedEventArgs* pArgs) { RaiseEvent(pArgs); }
    virtual void OnUnchecked(RoutedEventArgs* pArgs) { RaiseEvent(pArgs); }
    virtual void OnVisualParentChanged(DependencyObject* pOldParent);

    void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs* e) AVUI_OVERRIDE;

private:
    static void StaticConstructor();

    bool get_InsideContextMenu() { return UnboxValue<bool>(GetValue(get_InsideContextMenuProperty())); }

    static void SetInsideContextMenuProperty(UIElement* pElement, bool value)
    {
        pElement->SetValue(get_InsideContextMenuProperty(), BoxValue(value));
    } 

    static void OnIsSelectedChanged(Object* pSender, RoutedPropertyChangedBoolEventArgs* pArgs);

    bool get_OpenOnMouseEnter();
    bool MenuItemNavigate(Key::Enum key, ModifierKeys::Enum modifiers);
    void MouseEnterHelper();
    void ClickItem(bool userInitiated);
    void ClickItem();
    bool OpenMenu();
    void ClickHeader();
    void HandleMouseUp(MouseButtonEventArgs* pArgs);
    void HandleMouseDown(MouseButtonEventArgs* pArgs);
    void NotifySiblingsToSuspendAnimation();
    void NotifyChildrenToResumeAnimation();
    void SetMenuMode(bool menuMode);
    void UpdateCanExecute();
    void FocusOrSelect();
    void UpdateIsPressed();
    void OpenSubmenuWithKeyboard();

    void OpenOnLoad(Object* pSender, RoutedEventArgs* pArgs);
    void RegisterToOpenOnLoad();
    static void PrepareMenuItem(DependencyObject* pElement, Object* pItem);

    DECLARE_DEPENDENCY_PROPERTY(IsSelected);
    bool get_IsSelected() { return UnboxValue< bool >(GetValue(get_IsSelectedProperty())); }
    void set_IsSelected(const bool& value) { SetValue(get_IsSelectedProperty(), AVUI::BoxValue(value)); }

    static void OnIsSelectedChanged(DependencyObject* pDO, DependencyPropertyChangedEventArgs* e);


    Object* get_MenuLogicalParent();

    MenuItem* get_CurrentSelection() { return m_pCurrentSelection; }
    void set_CurrentSelection(MenuItem* pValue);

    class BoolField
    {
    public:
        enum Enum
        {
            OpenedWithKeyboard = 1,
            IgnoreNextMouseLeave = 2,
            IgnoreMouseEvents = 4,
            MouseEnterOnMouseMove = 8,
            CanExecuteInvalid = 0x10
        };
    };

    bool GetBoolField(unsigned int field)
    {
        return ((m_menuItemBoolFieldStore & field) != 0);
    }

    void SetBoolField(unsigned int field, bool value)
    {
        if (value)
        {
            m_menuItemBoolFieldStore |= field;
        }
        else
        {
            m_menuItemBoolFieldStore &= ~field;
        }
    }

    void OnPopupClosed(Object* pSource, EventArgs* pArgs); 

    void InvokeClickAfterRender(Object* pSender, Object* pArg);
    virtual void OnClickCore(bool userInitiated) { OnClick(); }
    void OnClickImpl(bool userInitiated);

    static void OnIsCheckableChanged(DependencyObject* pTarget, DependencyPropertyChangedEventArgs* e);
    static void OnIsCheckedChanged(DependencyObject* pDO, DependencyPropertyChangedEventArgs* e);
    static void OnIsSubmenuOpenChanged(DependencyObject* pDO, DependencyPropertyChangedEventArgs* e);
    static ObjectPtr CoerceIsSubmenuOpen(DependencyObject* pDO, Object* pValue);

    void UpdateRole();
    void StopTimer(TRefCountedPtr<DispatcherTimer> *ppTimer);
    void MouseEnterInMenuMode(MenuItemRole::Enum role);
    void set_IsHighlighted(bool isHighlighted) { SetValue(IsHighlightedPropertyKey, BoxValue(isHighlighted)); }
    void OpenHierarchy(MenuItemRole::Enum role);
    void MouseLeaveInMenuMode(MenuItemRole::Enum role);

    bool get_IsInMenuMode();
    bool get_IsMouseOverSibling();
    static bool IsMouseReallyOver(UIElement* pElement);
    MenuItem* get_CurrentSibling();
    void SetTimerToOpenHierarchy();
    void StartTimer(DispatcherTimer* pTimer);
    void SetTimerToCloseHierarchy();

    void SetBoolFieldAnonymousDelegate(Object* pSender, Object* pArg);
    void CoerceIsSubmenuOpenAnonymousDelegate(Object* pSender, Object* pArg);
    void SetIsSuspendingAnonymousDelegate(Object* pSender, Object* pArg);
    void OpenHierarchyAnonymousDelegate(Object* pSender, EventArgs* pArg);
    void CloseHierarchyAnonymousDelegate(Object* pSender, EventArgs* pArg);


    TRefCountedPtr<Popup> m_pSubmenuPopup;
    bool m_userInitiatedPress;
    TRefCountedPtr<DispatcherTimer> m_pOpenHierarchyTimer;
    TRefCountedPtr<DispatcherTimer> m_pCloseHierarchyTimer;
    unsigned int m_menuItemBoolFieldStore;
    TRefCountedPtr<MenuItem> m_pCurrentSelection;

};


}; // namespace AVUI


